home *** CD-ROM | disk | FTP | other *** search
/ Clickx 47 / Clickx 47.iso / assets / software / Miro_Installer.exe / components / pybridge.py < prev    next >
Encoding:
Python Source  |  2008-01-10  |  32.4 KB  |  882 lines

  1. # Miro - an RSS based video player application
  2. # Copyright (C) 2005-2007 Participatory Culture Foundation
  3. #
  4. # This program is free software; you can redistribute it and/or modify
  5. # it under the terms of the GNU General Public License as published by
  6. # the Free Software Foundation; either version 2 of the License, or
  7. # (at your option) any later version.
  8. #
  9. # This program is distributed in the hope that it will be useful,
  10. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12. # GNU General Public License for more details.
  13. #
  14. # You should have received a copy of the GNU General Public License
  15. # along with this program; if not, write to the Free Software
  16. # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
  17.  
  18. from gettext import gettext as _
  19. from xpcom import components
  20. import ctypes
  21. import os
  22. import shutil
  23. import sys
  24. import traceback
  25. import _winreg
  26.  
  27. try:
  28.     import platformutils
  29.     platformutils.initializeLocale()
  30.     platformutils.setupLogging()
  31.     import gtcache
  32.     gtcache.init()
  33.     import app
  34.     import autoupdate
  35.     import eventloop
  36.     import config
  37.     import dialogs
  38.     import folder
  39.     import playlist
  40.     import prefs
  41.     import platformcfg
  42.     import singleclick
  43.     import frontend
  44.     import util
  45.     import menubar
  46.     import feed
  47.     import database
  48.     from frontend_implementation import HTMLDisplay
  49.     from frontend_implementation.UIBackendDelegate import UIBackendDelegate
  50.     from eventloop import asUrgent, asIdle
  51.     from platformutils import getLongPathName
  52.     import searchengines
  53.     import views
  54.     import moviedata
  55.     import migrateappname
  56.     import keyboard
  57.     moviedata.RUNNING_MAX = 1
  58. except:
  59.     errorOnImport = True
  60.     # get a fallback error message in case we can't import util either
  61.     import traceback
  62.     importErrorMessage = (_("Error importing modules:\n%s") %
  63.         traceback.format_exc())
  64.     try:
  65.         import util
  66.         importErrorMessage = util.failed(_("Starting up"),
  67.                 withExn=True)
  68.     except:
  69.         raise
  70.     # we need to make a fake asUrgent since we probably couldn't import
  71.     # eventloop.
  72.     def asUrgent(func):
  73.         return func
  74.     def asIdle(func):
  75.         return func
  76. else:
  77.     errorOnImport = False
  78.  
  79. # See http://www.xulplanet.com/tutorials/xultu/keyshort.html
  80. XUL_MOD_STRINGS = {menubar.CTRL : 'control',
  81.                    menubar.ALT:   'alt',
  82.                    menubar.SHIFT: 'shift'}
  83. XUL_KEY_STRINGS ={ menubar.RIGHT_ARROW: 'VK_RIGHT',
  84.                    menubar.LEFT_ARROW:  'VK_LEFT',
  85.                    menubar.UP_ARROW:    'VK_UP',
  86.                    menubar.DOWN_ARROW:  'VK_DOWN',
  87.                    menubar.SPACE : 'VK_SPACE',
  88.                    menubar.ENTER: 'VK_ENTER',
  89.                    menubar.DELETE: 'VK_DELETE',
  90.                    menubar.BKSPACE: 'VK_BACK',
  91.                    menubar.F1: 'VK_F1',
  92.                    menubar.F2: 'VK_F2',
  93.                    menubar.F3: 'VK_F3',
  94.                    menubar.F4: 'VK_F4',
  95.                    menubar.F5: 'VK_F5',
  96.                    menubar.F6: 'VK_F6',
  97.                    menubar.F7: 'VK_F7',
  98.                    menubar.F8: 'VK_F8',
  99.                    menubar.F9: 'VK_F9',
  100.                    menubar.F10: 'VK_F10',
  101.                    menubar.F11: 'VK_F11',
  102.                    menubar.F12: 'VK_F12'}
  103.  
  104. # Extent the ShortCut class to include a XULString() function
  105. def XULKey(shortcut):
  106.     if isinstance(shortcut.key, int):
  107.         return XUL_KEY_STRINGS[shortcut.key]
  108.     else:
  109.         return shortcut.key.upper()
  110.  
  111. def XULModifier(shortcut):
  112.     if shortcut.key is None:
  113.         return None
  114.     output = []
  115.     for modifier in shortcut.modifiers:
  116.         output.append(XUL_MOD_STRINGS[modifier])
  117.     return ' '.join(output)
  118.  
  119. def XULDisplayedShortcut(item):
  120.     """Return the index of the default shortcut.  Normally items only have 1
  121.     shortcut, which is an easy case.  If items have multiple shortcuts,
  122.     normally we display the last one in the menus, however there are some
  123.     special cases.
  124.  
  125.     Shortcut indicies are 1-based
  126.     """
  127.  
  128.     if item.action.startswith("Remove"):
  129.         return 1
  130.     return len(item.shortcuts)
  131.  
  132. nsIEventQueueService = components.interfaces.nsIEventQueueService
  133. nsIProperties = components.interfaces.nsIProperties
  134. nsIFile = components.interfaces.nsIFile
  135. nsIProxyObjectManager = components.interfaces.nsIProxyObjectManager
  136. pcfIDTVPyBridge = components.interfaces.pcfIDTVPyBridge
  137. pcfIDTVJSBridge = components.interfaces.pcfIDTVJSBridge
  138. pcfIDTVVLCRenderer = components.interfaces.pcfIDTVVLCRenderer
  139.  
  140. def makeComp(clsid, iid):
  141.     """Helper function to get an XPCOM component"""
  142.     return components.classes[clsid].createInstance(iid)
  143.  
  144. def makeService(clsid, iid):
  145.     """Helper function to get an XPCOM service"""
  146.     return components.classes[clsid].getService(iid)
  147.  
  148. def createProxyObjects():
  149.     """Creates the jsbridge and vlcrenderer xpcom components, then wraps them in
  150.     a proxy object, then stores them in the frontend module.  By making them
  151.     proxy objects, we ensure that the calls to them get made in the xul event
  152.     loop.
  153.     """
  154.  
  155.     proxyManager = makeComp("@mozilla.org/xpcomproxy;1",
  156.             nsIProxyObjectManager)
  157.     eventQueueService = makeService("@mozilla.org/event-queue-service;1",
  158.             nsIEventQueueService)
  159.     xulEventQueue = eventQueueService.getSpecialEventQueue(
  160.             nsIEventQueueService.UI_THREAD_EVENT_QUEUE)
  161.  
  162.     jsBridge = makeService("@participatoryculture.org/dtv/jsbridge;1",
  163.             pcfIDTVJSBridge)
  164.     frontend.jsBridge = proxyManager.getProxyForObject(xulEventQueue,
  165.             pcfIDTVJSBridge, jsBridge, nsIProxyObjectManager.INVOKE_ASYNC |
  166.             nsIProxyObjectManager.FORCE_PROXY_CREATION)
  167.  
  168.     vlcRenderer = makeService("@participatoryculture.org/dtv/vlc-renderer;1",
  169.             pcfIDTVVLCRenderer)
  170.     frontend.vlcRenderer = proxyManager.getProxyForObject(xulEventQueue,
  171.             pcfIDTVVLCRenderer, vlcRenderer, 
  172.             nsIProxyObjectManager.INVOKE_SYNC |
  173.             nsIProxyObjectManager.FORCE_PROXY_CREATION)
  174.  
  175. def initializeProxyObjects(window):
  176.     frontend.vlcRenderer.init(window)
  177.     frontend.jsBridge.init(window)
  178.  
  179. def initializeHTTPProxy():
  180.     klass = components.classes["@mozilla.org/preferences-service;1"]
  181.     xulprefs = klass.getService(components.interfaces.nsIPrefService)
  182.     branch = xulprefs.getBranch("network.proxy.")
  183.     if config.get(prefs.HTTP_PROXY_ACTIVE):                     
  184.         branch.setIntPref("type",1)
  185.         branch.setCharPref("http", config.get(prefs.HTTP_PROXY_HOST))
  186.         branch.setCharPref("ssl", config.get(prefs.HTTP_PROXY_HOST))
  187.         branch.setIntPref("http_port", config.get(prefs.HTTP_PROXY_PORT))
  188.         branch.setIntPref("ssl_port", config.get(prefs.HTTP_PROXY_PORT))
  189.         branch.setBoolPref("share_proxy_settings", True)
  190.     else:
  191.         branch.setIntPref("type",0)
  192.  
  193. def registerHttpObserver():
  194.     observer = makeComp("@participatoryculture.org/dtv/httprequestobserver;1",
  195.         components.interfaces.nsIObserver)
  196.     observer_service = makeService("@mozilla.org/observer-service;1",
  197.             components.interfaces.nsIObserverService)
  198.     observer_service.addObserver(observer, "http-on-modify-request", False);
  199.         
  200. def getArgumentList(commandLine):
  201.     """Convert a nsICommandLine component to a list of arguments to pass
  202.     to the singleclick module."""
  203.  
  204.     args = [commandLine.getArgument(i) for i in range(commandLine.length)]
  205.     # filter out the application.ini that gets included
  206.     if len(args) > 0 and args[0].lower().endswith('application.ini'):
  207.         args = args[1:]
  208.     return [getLongPathName(path) for path in args]
  209.  
  210. # Copied from resources.py; if you change this function here, change it
  211. # there too.
  212. def appRoot():
  213.     klass = components.classes["@mozilla.org/file/directory_service;1"]
  214.     service = klass.getService(nsIProperties)
  215.     file = service.get("XCurProcD", nsIFile)
  216.     return file.path
  217.  
  218. # Functions to convert menu information into XUL form
  219. def XULifyLabel(label):
  220.     return label.replace(u'_',u'')
  221. def XULAccelFromLabel(label):
  222.     parts = label.split(u'_')
  223.     if len(parts) > 1:
  224.         return parts[1][0]
  225.  
  226.  
  227. def prefsChangeCallback(mapped, id):
  228.     if isinstance (mapped.actualFeed, feed.DirectoryWatchFeedImpl):
  229.         frontend.jsBridge.directoryWatchAdded (str(id), mapped.dir, mapped.visible);
  230.  
  231. def prefsRemoveCallback(mapped, id):
  232.     if isinstance (mapped.actualFeed, feed.DirectoryWatchFeedImpl):
  233.         frontend.jsBridge.directoryWatchRemoved (str(id));
  234.  
  235.  
  236. @asIdle
  237. def startPrefs():
  238.     for f in views.feeds:
  239.         if isinstance (f.actualFeed, feed.DirectoryWatchFeedImpl):
  240.             frontend.jsBridge.directoryWatchAdded (str(f.getID()), f.dir, f.visible)
  241.     views.feeds.addChangeCallback(prefsChangeCallback)
  242.     views.feeds.addAddCallback(prefsChangeCallback)
  243.     views.feeds.addRemoveCallback(prefsRemoveCallback)
  244.  
  245. @asIdle
  246. def endPrefs():
  247.     views.feeds.removeChangeCallback(prefsChangeCallback)
  248.     views.feeds.removeAddCallback(prefsChangeCallback)
  249.     views.feeds.removeRemoveCallback(prefsRemoveCallback)
  250.  
  251.  
  252. class PyBridge:
  253.     _com_interfaces_ = [pcfIDTVPyBridge]
  254.     _reg_clsid_ = "{F87D30FF-C117-401e-9194-DF3877C926D4}"
  255.     _reg_contractid_ = "@participatoryculture.org/dtv/pybridge;1"
  256.     _reg_desc_ = "Bridge into DTV Python core"
  257.  
  258.     def __init__(self):
  259.         migrateappname.migrateSupport('Democracy Player', 'Miro')
  260.         self.started = False
  261.         self.cursorDisplayCount = 0
  262.         if not errorOnImport:
  263.             self.delegate = UIBackendDelegate()
  264.  
  265.     def getStartupError(self):
  266.         if not errorOnImport:
  267.             return ""
  268.         else:
  269.             return importErrorMessage
  270.  
  271.     def onStartup(self, window):
  272.         if self.started:
  273.             util.failed(_("Loading window"), 
  274.                 details=_("onStartup called twice"))
  275.             return
  276.         else:
  277.             self.started = True
  278.  
  279.         initializeProxyObjects(window)
  280.         registerHttpObserver()
  281.         app.main()
  282.         initializeHTTPProxy()
  283.  
  284.     @asUrgent
  285.     def initializeViews(self):
  286.         views.initialize()
  287.  
  288.     def onShutdown(self):
  289.         frontend.vlcRenderer.stop()
  290.         app.controller.onShutdown()
  291.  
  292.     def deleteVLCCache(self):
  293.         appDataPath = platformcfg.getSpecialFolder("AppData")
  294.         if appDataPath:
  295.             vlcCacheDir = os.path.join(appDataPath, "PCF-VLC")
  296.             shutil.rmtree(vlcCacheDir, ignore_errors=True)
  297.  
  298.     def shortenDirectoryName(self, path):
  299.         """Shorten a directory name by recognizing well-known nicknames, like
  300.         "Desktop", and "My Documents"
  301.         """
  302.  
  303.         tries = [ "My Music", "My Pictures", "My Videos", "My Documents",
  304.             "Desktop", 
  305.         ]
  306.  
  307.         for name in tries:
  308.             virtualPath = platformcfg.getSpecialFolder(name)
  309.             if virtualPath is None:
  310.                 continue
  311.             if path == virtualPath:
  312.                 return name
  313.             elif path.startswith(virtualPath):
  314.                 relativePath = path[len(virtualPath):]
  315.                 if relativePath.startswith("\\"):
  316.                     return name + relativePath
  317.                 else:
  318.                     return "%s\\%s" % (name, relativePath)
  319.         return path
  320.  
  321.  
  322.     # Preference setters/getters.
  323.     #
  324.     # NOTE: these are not in the mail event loop, so we have to be careful in
  325.     # accessing data.  config.get and config.set are threadsafe though.
  326.     #
  327.     def getRunAtStartup(self):
  328.         return config.get(prefs.RUN_AT_STARTUP)
  329.     def setRunAtStartup(self, value):
  330.         self.delegate.setRunAtStartup(value)
  331.         config.set(prefs.RUN_AT_STARTUP, value)
  332.     def getCheckEvery(self):
  333.         return config.get(prefs.CHECK_CHANNELS_EVERY_X_MN)
  334.     def setCheckEvery(self, value):
  335.         return config.set(prefs.CHECK_CHANNELS_EVERY_X_MN, value)
  336.     def getMoviesDirectory(self):
  337.         return config.get(prefs.MOVIES_DIRECTORY)
  338.     def changeMoviesDirectory(self, path, migrate):
  339.         app.changeMoviesDirectory(path, migrate)
  340.     def getLimitUpstream(self):
  341.         return config.get(prefs.LIMIT_UPSTREAM)
  342.     def setLimitUpstream(self, value):
  343.         config.set(prefs.LIMIT_UPSTREAM, value)
  344.     def getLimitUpstreamAmount(self):
  345.         return config.get(prefs.UPSTREAM_LIMIT_IN_KBS)
  346.     def setLimitUpstreamAmount(self, value):
  347.         return config.set(prefs.UPSTREAM_LIMIT_IN_KBS, value)
  348.     def getLimitDownstream(self):
  349.         return config.get(prefs.LIMIT_DOWNSTREAM_BT)
  350.     def setLimitDownstream(self, value):
  351.         config.set(prefs.LIMIT_DOWNSTREAM_BT, value)
  352.     def getLimitDownstreamAmount(self):
  353.         return config.get(prefs.DOWNSTREAM_BT_LIMIT_IN_KBS)
  354.     def setLimitDownstreamAmount(self, value):
  355.         return config.set(prefs.DOWNSTREAM_BT_LIMIT_IN_KBS, value)
  356.     def getMaxManual(self):
  357.         return config.get(prefs.MAX_MANUAL_DOWNLOADS)
  358.     def setMaxManual(self, value):
  359.         return config.set(prefs.MAX_MANUAL_DOWNLOADS, value)
  360.     def startPrefs(self):
  361.         startPrefs()
  362.     def updatePrefs(self):
  363.         endPrefs()
  364.     def getPreserveDiskSpace(self):
  365.         return config.get(prefs.PRESERVE_DISK_SPACE)
  366.     def setPreserveDiskSpace(self, value):
  367.         config.set(prefs.PRESERVE_DISK_SPACE, value)
  368.     def getPreserveDiskSpaceAmount(self):
  369.         return config.get(prefs.PRESERVE_X_GB_FREE)
  370.     def setPreserveDiskSpaceAmount(self, value):
  371.         return config.set(prefs.PRESERVE_X_GB_FREE, value)
  372.     def getExpireAfter(self):
  373.         return config.get(prefs.EXPIRE_AFTER_X_DAYS)
  374.     def setExpireAfter(self, value):
  375.         return config.set(prefs.EXPIRE_AFTER_X_DAYS, value)
  376.     def getSinglePlayMode(self):
  377.         return config.get(prefs.SINGLE_VIDEO_PLAYBACK_MODE)
  378.     def setSinglePlayMode(self, value):
  379.         return config.set(prefs.SINGLE_VIDEO_PLAYBACK_MODE, value)
  380.     def getResumeVideosMode(self):
  381.         return config.get(prefs.RESUME_VIDEOS_MODE)
  382.     def setResumeVideosMode(self, value):
  383.         return config.set(prefs.RESUME_VIDEOS_MODE, value)
  384.     def getBTMinPort(self):
  385.         return config.get(prefs.BT_MIN_PORT)
  386.     def setBTMinPort(self, value):
  387.         return config.set(prefs.BT_MIN_PORT, value)
  388.     def getBTMaxPort(self):
  389.         return config.get(prefs.BT_MAX_PORT)
  390.     def setBTMaxPort(self, value):
  391.         return config.set(prefs.BT_MAX_PORT, value)
  392.     def getStartupTasksDone(self):
  393.         return config.get(prefs.STARTUP_TASKS_DONE)
  394.     def setStartupTasksDone(self, value):
  395.         return config.set(prefs.STARTUP_TASKS_DONE, value)
  396.     def getWarnIfDownloadingOnQuit(self):
  397.         return config.get(prefs.WARN_IF_DOWNLOADING_ON_QUIT)
  398.     def setWarnIfDownloadingOnQuit(self, value):
  399.         return config.set(prefs.WARN_IF_DOWNLOADING_ON_QUIT, value)
  400.     def getUseUpnp(self):
  401.         return config.get(prefs.USE_UPNP)
  402.     def setUseUpnp(self, value):
  403.         config.set(prefs.USE_UPNP, value)
  404.     def getBitTorrentEncReq(self):
  405.         return config.get(prefs.BT_ENC_REQ)
  406.     def setBitTorrentEncReq(self, value):
  407.         config.set(prefs.BT_ENC_REQ, value)
  408.  
  409.     def handleCommandLine(self, commandLine):
  410.         # Here's a massive hack to get command line parameters into config
  411.         args = getArgumentList(commandLine)
  412.         for x in range(len(args)-1):
  413.             if args[x] == '--theme':
  414.                 config.__currentThemeHack = args[x+1]
  415.                 config.load(theme = args[x+1])
  416.                 break
  417.  
  418.         # Doesn't matter if this executes before the call to
  419.         # parseCommandLineArgs in app.py. -clahey
  420.         self._handleCommandLine(commandLine)
  421.  
  422.     @asUrgent
  423.     def _handleCommandLine(self, commandLine):
  424.         singleclick.handleCommandLineArgs(getArgumentList(commandLine))
  425.  
  426.     def pageLoadFinished(self, area, url):
  427.         eventloop.addUrgentCall(HTMLDisplay.runPageFinishCallback, 
  428.                 "%s finish callback" % area, args=(area, url))
  429.  
  430.     @asUrgent
  431.     def openFile(self, path):
  432.         singleclick.openFile(getLongPathName(path))
  433.  
  434.     @asUrgent
  435.     def setVolume(self, volume):
  436.         volume = float(volume)
  437.         config.set(prefs.VOLUME_LEVEL, volume)
  438.         if hasattr(app.controller, 'videoDisplay'):
  439.             app.controller.videoDisplay.setVolume(volume, moveSlider=False)
  440.  
  441.     @asUrgent
  442.     def quit(self):
  443.         if app.controller.finishedStartup:
  444.             app.controller.quit()
  445.  
  446.     @asUrgent
  447.     def removeCurrentChannel(self):
  448.         app.controller.removeCurrentFeed()
  449.  
  450.     @asUrgent
  451.     def updateCurrentChannel(self):
  452.         app.controller.updateCurrentFeed()
  453.  
  454.     @asUrgent
  455.     def updateChannels(self):
  456.         app.controller.updateAllFeeds()
  457.  
  458.     @asUrgent
  459.     def showHelp(self):
  460.         self.delegate.openExternalURL(config.get(prefs.HELP_URL))
  461.  
  462.     @asUrgent
  463.     def reportBug(self):
  464.         self.delegate.openExternalURL(config.get(prefs.BUG_REPORT_URL))
  465.  
  466.     @asUrgent
  467.     def copyChannelLink(self):
  468.         app.controller.copyCurrentFeedURL()
  469.  
  470.     @asUrgent
  471.     def handleContextMenu(self, index):
  472.         self.delegate.handleContextMenu(index)
  473.  
  474.     @asUrgent
  475.     def handleSimpleDialog(self, id, buttonIndex):
  476.         self.delegate.handleDialog(id, buttonIndex)
  477.  
  478.     @asUrgent
  479.     def handleCheckboxDialog(self, id, buttonIndex, checkbox_value):
  480.         self.delegate.handleDialog(id, buttonIndex,
  481.                 checkbox_value=checkbox_value)
  482.     @asUrgent
  483.     def handleCheckboxTextboxDialog(self, id, buttonIndex, checkbox_value,
  484.                                     textbox_value):
  485.         self.delegate.handleDialog(id, buttonIndex,
  486.                                    checkbox_value=checkbox_value,
  487.                                    textbox_value=textbox_value)
  488.  
  489.     @asUrgent
  490.     def handleHTTPAuthDialog(self, id, buttonIndex, username, password):
  491.         self.delegate.handleDialog(id, buttonIndex, username=username,
  492.                 password=password)
  493.  
  494.     @asUrgent
  495.     def handleTextEntryDialog(self, id, buttonIndex, text):
  496.         self.delegate.handleDialog(id, buttonIndex, value=text)
  497.  
  498.     @asUrgent
  499.     def handleSearchChannelDialog(self, id, buttonIndex, term, style, loc):
  500.         self.delegate.handleDialog(id, buttonIndex, term=term, style=style, loc=loc)
  501.     @asUrgent
  502.     def handleFileDialog(self, id, pathname):
  503.         self.delegate.handleFileDialog(id, pathname)
  504.  
  505.     @asUrgent
  506.     def addChannel(self, url):
  507.         app.controller.addAndSelectFeed(url)
  508.  
  509.     @asUrgent
  510.     def openURL(self, url):
  511.         self.delegate.openExternalURL(url)
  512.  
  513.     @asUrgent
  514.     def playPause(self):
  515.         app.controller.playbackController.playPause()
  516.  
  517.     @asUrgent
  518.     def pause(self):
  519.         if hasattr(app.controller, 'playbackController'):
  520.             app.controller.playbackController.pause()
  521.  
  522.     @asUrgent
  523.     def stop(self):
  524.         app.controller.playbackController.stop()
  525.  
  526.     @asUrgent
  527.     def skip(self, step):
  528.         app.controller.playbackController.skip(step)
  529.  
  530.     @asUrgent
  531.     def skipPrevious(self):
  532.         app.controller.playbackController.skip(-1, allowMovieReset=False)
  533.  
  534.     @asUrgent
  535.     def onMovieFinished(self):
  536.         app.controller.playbackController.onMovieFinished()
  537.  
  538.     @asUrgent
  539.     def loadURLInBrowser(self, browserId, url):
  540.         try:
  541.             display = app.controller.frame.selectedDisplays[browserId]
  542.         except KeyError:
  543.             print "No HTMLDisplay for %s in loadURLInBrowser: "% browserId
  544.         else:
  545.             display.onURLLoad(url)
  546.  
  547.     @asUrgent
  548.     def performSearch(self, engine, query):
  549.         app.controller.performSearch(engine, query)
  550.  
  551.     # Returns a list of search engine titles and names
  552.     # Should we just keep a map of engines to names?
  553.     def getSearchEngineNames(self):
  554.         out = []
  555.         for engine in views.searchEngines:
  556.             out.append(engine.name)
  557.         return out
  558.     def getSearchEngineTitles(self):
  559.         out = []
  560.         for engine in views.searchEngines:
  561.             out.append(engine.title)
  562.         return out
  563.  
  564.     def showCursor(self, display):
  565.         # ShowCursor has an amazing API.  From Microsoft:
  566.         #
  567.         # This function sets an internal display counter that determines
  568.         # whether the cursor should be displayed. The cursor is displayed
  569.         # only if the display count is greater than or equal to 0. If a
  570.         # mouse is installed, the initial display count is 0.  If no mouse
  571.         # is installed, the display count is -1
  572.         #
  573.         # How do we get the initial display count?  There's no method.  We
  574.         # assume it's 0 and the mouse is plugged in.
  575.         if ((display and self.cursorDisplayCount >= 0) or
  576.                 (not display and self.cursorDisplayCount < 0)):
  577.             return
  578.         if display:
  579.             arg = 1
  580.         else:
  581.             arg = 0
  582.         self.cursorDisplayCount = ctypes.windll.user32.ShowCursor(arg)
  583.  
  584.     @asUrgent
  585.     def createNewPlaylist(self):
  586.         playlist.createNewPlaylist()
  587.  
  588.     @asUrgent
  589.     def createNewPlaylistFolder(self):
  590.         folder.createNewPlaylistFolder()
  591.  
  592.     @asUrgent
  593.     def createNewSearchChannel(self):
  594.         app.controller.addSearchFeed()
  595.  
  596.     @asUrgent
  597.     def createNewChannelFolder(self):
  598.         folder.createNewChannelFolder()
  599.  
  600.     @asUrgent
  601.     def handleDrop(self, dropData, dropType, sourceData):
  602.         app.controller.handleDrop(dropData, dropType, sourceData)
  603.  
  604.  
  605.     @asUrgent
  606.     def removeCurrentSelection(self):
  607.         app.controller.removeCurrentSelection()
  608.  
  609.     
  610.     @asUrgent
  611.     def checkForUpdates(self):
  612.         autoupdate.checkForUpdates()
  613.  
  614.     @asUrgent
  615.     def removeCurrentItems(self):
  616.         app.controller.removeCurrentItems()
  617.  
  618.     @asUrgent
  619.     def copyCurrentItemURL(self):
  620.         app.controller.copyCurrentItemURL()
  621.  
  622.     @asUrgent
  623.     def selectAllItems(self):
  624.         app.controller.selectAllItems()
  625.  
  626.     @asUrgent
  627.     def createNewChannelFolder(self):
  628.         folder.createNewChannelFolder()
  629.  
  630.     @asUrgent
  631.     def createNewChannelGuide(self):
  632.         app.controller.addAndSelectGuide()
  633.  
  634.     @asUrgent
  635.     def createNewDownload(self):
  636.         app.controller.newDownload()
  637.  
  638.     @asUrgent
  639.     def importChannels(self):
  640.         app.controller.importChannels()
  641.  
  642.     @asUrgent
  643.     def exportChannels(self):
  644.         app.controller.exportChannels()
  645.  
  646.     @asUrgent
  647.     def addChannel(self):
  648.         app.controller.addAndSelectFeed()
  649.  
  650.     @asUrgent
  651.     def renameCurrentChannel(self):
  652.         app.controller.renameCurrentChannel()
  653.  
  654.     @asUrgent
  655.     def recommendCurrentChannel(self):
  656.         app.controller.recommendCurrentFeed()
  657.  
  658.     @asUrgent
  659.     def renameCurrentPlaylist(self):
  660.         app.controller.renameCurrentPlaylist()
  661.  
  662.     @asUrgent
  663.     def removeCurrentPlaylist(self):
  664.         app.controller.removeCurrentPlaylist()
  665.  
  666.     def openDonatePage(self):
  667.         self.delegate.openExternalURL(config.get(prefs.DONATE_URL))
  668.  
  669.     def openBugTracker(self):
  670.         self.delegate.openExternalURL(config.get(prefs.BUG_TRACKER_URL))
  671.  
  672.     @asUrgent
  673.     def saveVideoFile(self, path):
  674.         if frontend.currentVideoPath is None:
  675.             return
  676.         app.saveVideo(frontend.currentVideoPath, path)
  677.  
  678.     def startupDoSearch(self, path):
  679.         if path.endswith(":"):
  680.             path = path + "\\" # convert C: to C:\
  681.         frontend.startup.doSearch(path)
  682.  
  683.     def startupCancelSearch(self):
  684.         frontend.startup.cancelSearch()
  685.  
  686.     def getSpecialFolder(self, name):
  687.         return platformcfg.getSpecialFolder(name)
  688.  
  689.     def extractFinish (self, duration, screenshot_success):
  690.         app.controller.videoDisplay.extractFinish(duration, screenshot_success)
  691.  
  692.     def createProxyObjects(self):
  693.         createProxyObjects()
  694.  
  695.     def printOut(self, output):
  696.         print output
  697.  
  698.     def addMenubar(self, document):
  699.         menubarElement = document.getElementById("titlebar-menu")
  700.         trayMenuElement = document.getElementById("traypopup")
  701.         keysetElement = document.createElementNS(
  702.          "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul","keyset")
  703.  
  704.         for menu in (menubar.menubar.menus + (menubar.traymenu,)):
  705.             for item in menu.menuitems:
  706.                 if isinstance(item, menubar.MenuItem):
  707.                     count = 0
  708.                     for shortcut in item.shortcuts:
  709.                         count += 1
  710.                         keyElement = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul","key")
  711.                         keyElement.setAttribute("id", "%s-key%d" % (item.action, count))
  712.                         if len(XULKey(shortcut)) == 1:
  713.                             keyElement.setAttribute("key", XULKey(shortcut))
  714.                         else:
  715.                             keyElement.setAttribute("keycode", XULKey(shortcut))
  716.                         if XULKey(shortcut) == 'VK_SPACE':
  717.                             # spacebar doesn't get display text for some reason
  718.                             keyElement.setAttribute('keytext', _('Spacebar'))
  719.                         if len(shortcut.modifiers) > 0:
  720.                             keyElement.setAttribute("modifiers", XULModifier(shortcut))
  721.                         keyElement.setAttribute("command", item.action)
  722.                         keysetElement.appendChild(keyElement)
  723.         menubarElement.appendChild(keysetElement)
  724.  
  725.         for menu in menubar.menubar.menus:
  726.             menuElement = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul","menu")
  727.             menuElement.setAttribute("id", "menu-%s" % menu.action.lower())
  728.             menuElement.setAttribute("label", XULifyLabel(menu.getLabel(menu.action)))
  729.             if XULAccelFromLabel(menu.getLabel(menu.action)):
  730.                 menuElement.setAttribute("accesskey", XULAccelFromLabel(menu.getLabel(menu.action)))
  731.             menupopup = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul","menupopup")
  732.  
  733.             menupopup.setAttribute("id", "menupopup-%s" % menu.action.lower())
  734.             menuElement.appendChild(menupopup)
  735.             menubarElement.appendChild(menuElement)
  736.             for item in menu.menuitems:
  737.                 if isinstance(item, menubar.Separator):
  738.                     menuitem = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul","menuseparator")
  739.                 else:
  740.                     menuitem = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul","menuitem")
  741.                     menuitem.setAttribute("id","menuitem-%s" % item.action.lower())
  742.                     menuitem.setAttribute("label",XULifyLabel(menu.getLabel(item.action)))
  743.                     menuitem.setAttribute("command", item.action)
  744.                     if XULAccelFromLabel(item.label):
  745.                         menuitem.setAttribute("accesskey",
  746.                                           XULAccelFromLabel(item.label))
  747.                     if len(item.shortcuts)>0:
  748.                         menuitem.setAttribute("key","%s-key%d"%(item.action,
  749.                             XULDisplayedShortcut(item)))
  750.                         
  751.                 menupopup.appendChild(menuitem)
  752.  
  753.         for item in menubar.traymenu.menuitems:
  754.             if isinstance(item, menubar.Separator):
  755.                 menuitem = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul","menuseparator")
  756.             else:
  757.                 menuitem = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul","menuitem")
  758.                 menuitem.setAttribute("id","traymenu-%s" % item.action.lower())
  759.                 menuitem.setAttribute("label",XULifyLabel(item.label))
  760.                 menuitem.setAttribute("command", item.action)
  761.                 if XULAccelFromLabel(item.label):
  762.                     menuitem.setAttribute("accesskey",
  763.                                           XULAccelFromLabel(item.label))
  764.                 if len(item.shortcuts)>0:
  765.                     menuitem.setAttribute("key","%s-key%d"%(item.action,len(item.shortcuts)))
  766.                         
  767.             trayMenuElement.appendChild(menuitem)
  768.  
  769.     # Grab the database information, then throw it over the fence to
  770.     # the UI thread
  771.     @asIdle
  772.     def updateTrayMenus(self):
  773.         if views.initialized:
  774.             numUnwatched = len(views.unwatchedItems)
  775.             numDownloading = len(views.downloadingItems)
  776.             numPaused = len(views.pausedItems)
  777.         else:
  778.             numPaused = numDownloading = numUnwatched = 0
  779.         frontend.jsBridge.updateTrayMenus(numUnwatched, numDownloading, numPaused)
  780.  
  781.     # HACK ALERT - We should change this to take a dictionary instead
  782.     # of all of the possible database variables. Since there's no
  783.     # equivalent in XPCOM, it would be a pain, so we can wait. -- NN
  784.     def getLabel(self,action,state,unwatched = 0,downloading = 0, paused = 0):
  785.         variables = {}
  786.         variables['numUnwatched'] = unwatched
  787.         variables['numDownloading'] = downloading
  788.         variables['numPaused'] = paused
  789.  
  790.         # Ih8XPCOM
  791.         if len(state) == 0:
  792.             state = None
  793.  
  794.         ret = XULifyLabel(menubar.menubar.getLabel(action,state,variables))
  795.         if ret == action:
  796.             ret = XULifyLabel(menubar.traymenu.getLabel(action,state, variables))
  797.         return ret
  798.  
  799.     @asIdle
  800.     def addDirectoryWatch(self, filename):
  801.         feed.Feed (u"dtv:directoryfeed:%s" % (platformutils.makeURLSafe(filename),))
  802.  
  803.     @asIdle
  804.     def removeDirectoryWatch(self, id):
  805.         try:
  806.             obj = database.defaultDatabase.getObjectByID (int(id))
  807.             app.controller.removeFeeds ([obj])
  808.     except:
  809.         pass
  810.  
  811.     @asIdle
  812.     def toggleDirectoryWatchShown(self, id):
  813.         try:
  814.             obj = database.defaultDatabase.getObjectByID (int(id))
  815.             obj.setVisible (not obj.visible)
  816.         except:
  817.             pass
  818.  
  819.     @asUrgent
  820.     def playUnwatched(self):
  821.         minimizer = makeService(
  822.             "@participatoryculture.org/dtv/minimize;1",
  823.             components.interfaces.pcfIDTVMinimize)
  824.         if minimizer.isMinimized():
  825.             minimizer.minimizeOrRestore()
  826.         app.controller.frame.mainDisplayCallback(u'action:playUnwatched')
  827.  
  828.     @asIdle
  829.     def pauseDownloads(self):
  830.         app.controller.frame.mainDisplayCallback(u'action:pauseAll')
  831.  
  832.     @asIdle
  833.     def resumeDownloads(self):
  834.         app.controller.frame.mainDisplayCallback(u'action:resumeAll')
  835.  
  836.     def minimizeToTray(self):
  837.         return config.get(prefs.MINIMIZE_TO_TRAY)
  838.  
  839.     def setMinimizeToTray(self, newSetting):
  840.         config.set(prefs.MINIMIZE_TO_TRAY, newSetting)
  841.  
  842.     def handleKeyPress(self, keycode, shiftDown, controlDown):
  843.         keycode_to_portable_code = {
  844.             37: keyboard.LEFT,
  845.             38: keyboard.UP,
  846.             39: keyboard.RIGHT,
  847.             40: keyboard.DOWN,
  848.         }
  849.         if keycode in keycode_to_portable_code:
  850.             key = keycode_to_portable_code[keycode]
  851.             keyboard.handleKey(key, shiftDown, controlDown)
  852.  
  853.     def handleCloseButton(self):
  854.         if not app.controller.finishedStartup:
  855.             return
  856.         if config.get(prefs.MINIMIZE_TO_TRAY_ASK_ON_CLOSE):
  857.             self.askUserForCloseBehaviour()
  858.         elif config.get(prefs.MINIMIZE_TO_TRAY):
  859.             minimizer = makeService(
  860.                     "@participatoryculture.org/dtv/minimize;1",
  861.                     components.interfaces.pcfIDTVMinimize)
  862.             minimizer.minimizeOrRestore()
  863.         else:
  864.             self.quit()
  865.  
  866.     def askUserForCloseBehaviour(self):
  867.         title = _("Close to tray?")
  868.         description = _("When you click the red close button, would you like Miro to close to the system tray or quit?  You can change this setting later in the Options.")
  869.  
  870.         dialog = dialogs.ChoiceDialog(title, description, 
  871.                 dialogs.BUTTON_CLOSE_TO_TRAY, dialogs.BUTTON_QUIT)
  872.         def callback(dialog):
  873.             if dialog.choice is None:
  874.                 return
  875.             if dialog.choice == dialogs.BUTTON_CLOSE_TO_TRAY:
  876.                 config.set(prefs.MINIMIZE_TO_TRAY, True)
  877.             else:
  878.                 config.set(prefs.MINIMIZE_TO_TRAY, False)
  879.             config.set(prefs.MINIMIZE_TO_TRAY_ASK_ON_CLOSE, False)
  880.             self.handleCloseButton()
  881.         dialog.run(callback)
  882.